// <copyright project="Salient.SqlServer.FluentNHibernate" file="DatabaseFnhFixture.cs" company="Sky Sanders">
// This source is a Public Domain Dedication.
// Please see http://spikes.codeplex.com/ for details.   
// Attribution is appreciated
// </copyright> 
// <version>1.0</version>

using System;
using System.Collections.Generic;
using System.IO;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate;
using Salient.SqlServer.Testing;

namespace Salient.SqlServer.FluentNHibernate.Testing
{
    public abstract class DatabaseFnhFixture : DatabaseFixture, IDisposable
    {
        private ISessionFactory _sessionFactory;

        protected DatabaseFnhFixture(string dataSource, string initialCatalog)
            : base(dataSource, initialCatalog)
        {
        }

        protected DatabaseFnhFixture(string dataSource, string initialCatalog, string userId, string password)
            : base(dataSource, initialCatalog, userId, password)
        {
        }

        protected DatabaseFnhFixture(string connectionString)
            : base(connectionString)
        {
        }

        protected ISessionFactory SessionFactory
        {
            get { return _sessionFactory; }
        }
        public override void TestFixtureTearDown()
        {
            DisposeSessionFactory();
            base.TestFixtureTearDown();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="mapReference">Any type in the namespace containing the fluent maps to use</param>
        protected void CreateSessionFactory(Type mapReference)
        {
            CreateSessionFactory(null, mapReference);
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="mappingFileOutputPath">Absolute path or path relative to output directory in which to write xml mapping files</param>
        /// <param name="mapReference">Any type in the namespace containing the fluent maps to use</param>
        protected void CreateSessionFactory(string mappingFileOutputPath, Type mapReference)
        {
            _sessionFactory = CreateSessionFactory(ConnectionString, mappingFileOutputPath, mapReference);
            Console.WriteLine("ConnectionString:{0}", ConnectionString);
            if (!string.IsNullOrEmpty(mappingFileOutputPath))
            {
                Console.WriteLine("Mapping files written to {0}", mappingFileOutputPath);
            }
        }

        /// <summary>
        /// Builds up a session from maps contained in a specific namespace against the specified database.
        /// </summary>
        /// <param name="connectionString"></param>
        /// <param name="mappingFileOutputPath">Absolute path or path relative to output directory in which to write xml mapping files</param>
        /// <param name="mapReference">Any type in the namespace containing the fluent maps to use</param>
        /// <returns></returns>
        private static ISessionFactory CreateSessionFactory(string connectionString, string mappingFileOutputPath,
                                                            Type mapReference)
        {
            var mappingTypes = new List<Type>();
            foreach (Type type in mapReference.Assembly.GetTypes())
            {
                if (type.Namespace == mapReference.Namespace)
                {
                    if (!type.ContainsGenericParameters) // exclude the generic base
                    {
                        mappingTypes.Add(type);
                    }
                }
            }

            return Fluently.Configure()
                .Database(MsSqlConfiguration.MsSql2008
                              .ConnectionString(connectionString)
                              .ShowSql())
                .Mappings(m =>
                              {
                                  if (!string.IsNullOrEmpty(mappingFileOutputPath))
                                  {
                                      if (!Directory.Exists(mappingFileOutputPath))
                                      {
                                          Directory.CreateDirectory(mappingFileOutputPath);
                                      }
                                      m.FluentMappings.ExportTo(mappingFileOutputPath);
                                  }

                                  foreach (Type mappingType in mappingTypes)
                                  {
                                      if (!string.IsNullOrEmpty(mappingFileOutputPath))
                                      {
                                          m.FluentMappings.Add(mappingType).ExportTo(mappingFileOutputPath);
                                      }
                                      else
                                      {
                                          m.FluentMappings.Add(mappingType);
                                      }
                                  }
                              })
                .BuildSessionFactory();
        }

        #region IDisposable

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                DisposeSessionFactory();
            }
        }

        protected void DisposeSessionFactory()
        {
            if (_sessionFactory != null)
            {
                _sessionFactory.Close();
                _sessionFactory.Dispose();
                _sessionFactory = null;
            }
        }

        #endregion
    }
}